home *** CD-ROM | disk | FTP | other *** search
/ HAM Radio 3.2 / Ham Radio Version 3.2 (Chestnut CD-ROMs)(1993).ISO / packet / n17jsrc / nntpcli.c < prev    next >
C/C++ Source or Header  |  1991-10-19  |  20KB  |  744 lines

  1. /*
  2.  *    Client routines for Network News Tranfer Protocol ala RFC977
  3.  *
  4.  *    Copyright 1990 Anders Klemets - SM0RGV, All Rights Reserved.
  5.  *    Permission granted for non-commercial copying and use, provided
  6.  *    this notice is retained.
  7.  *
  8.  *    Changes copyright 1990 Bernie Roehl, All Rights Reserved.
  9.  *    Permission granted for non-commercial copying and use, provided
  10.  *    this notice is retained.
  11.  *
  12.  *  Revision history:
  13.  *
  14.  *     May 11, 1990 - br checked for invalid chars in news filenames
  15.  *
  16.  *     May 10, 1990 - br changed date stamp in 'From ' lines to
  17.  *            seconds since GMT (to make parsing and expiry easier)
  18.  *
  19.  *     May 9, 1990 - br added locking of nntp.dat and history files,
  20.  *            second parameter to NNTP DIR, fixed bug in updating of
  21.  *            nntp.dat
  22.  *
  23.  *     early May, 1990 -- br added NNTP TRACE, NNTP DIR,
  24.  *            server-specific newsgroups and connection windows,
  25.  *            locking of newsgroup files using mlock() and rmlock(),
  26.  *            date stamping of 'From ' lines, increased stack space,
  27.  *            updating of nntp.dat only on successful sessions.
  28.  *
  29.  *     July 19, 1990 pa0gri Delinted and cleaned up. (calls and includes)
  30.  *
  31.  */
  32. #include <stdio.h>
  33. #include <sys/types.h>
  34. #include <time.h>
  35. #include <sys/timeb.h>
  36. #include <ctype.h>
  37. #include <string.h>  /* for strchr() */
  38. #ifdef    __TURBOC__
  39. #include <dir.h>
  40. #endif
  41. #include "global.h"
  42. #include "timer.h"
  43. #include "cmdparse.h"
  44. #include "commands.h"
  45. #include "socket.h"
  46. #include "usock.h"
  47. #include "netuser.h"
  48. #include "proc.h"
  49. #include "smtp.h"
  50. #include "files.h"
  51.  
  52. #define NNTPMAXLEN    512
  53.  
  54. static struct nntpservers {
  55.     struct timer nntpcli_t;
  56.     char *name;
  57.     char *groups;
  58.     int lowtime, hightime;  /* for connect window */
  59.     struct nntpservers *next;
  60. };
  61.  
  62. #define    NULLNNTP    (struct nntpservers *)NULL
  63.  
  64. #define MAXGROUPDIRS 10
  65.  
  66. static struct grouploc {
  67.     char *prefix;        /* e.g. comp, rec, net, talk, alt ... */
  68.     char *directory;     /* directory where these groups should be */
  69.     } groupdirs[MAXGROUPDIRS] = { NULL, NULL };
  70.  
  71. struct nntpservers *Nntpservers = NULLNNTP;
  72. static char *Nntpgroups = NULLCHAR;
  73. static unsigned short nntptrace = 1;
  74. static int nntpquiet = 0;
  75. static char *News_spool = NULL;
  76. static int np_all = 0;  /* non-zero if Newsdir is a malloc'ed space */
  77.  
  78. static char *validchars = "abcdefghijklmnopqrstuvwxyz0123456789-_";
  79.  
  80. static void nntptick __ARGS((void *tp));
  81. static void nntp_job __ARGS((int i1,void *tp,void *v1));
  82. static int gettxt __ARGS((int s,FILE *fp));
  83. static int getreply __ARGS((int s));
  84. static int getarticle __ARGS((int s,char *msgid));
  85. static int dogroups __ARGS((int argc,char *argv[],void *p));
  86. static int doadds __ARGS((int argc,char *argv[],void *p));
  87. static int dodrops __ARGS((int argc,char *argv[],void *p));
  88. static int dokicks __ARGS((int argc,char *argv[],void *p));
  89. static int dolists __ARGS((int argc,char *argv[],void *p));
  90. static int donntrace __ARGS((int argc,char *argv[],void *p));
  91. static int donnquiet __ARGS((int argc,char *argv[],void *p));
  92. static int dondir __ARGS((int argc,char *argv[],void *p));
  93.  
  94. /* Tracing levels:
  95.     0 - no tracing
  96.     1 - serious errors reported
  97.     2 - transient errors reported
  98.     3 - session progress reported
  99.     4 - actual received articles displayed
  100.  */
  101.  
  102. static struct cmds Nntpcmds[] = {
  103.     "addserver",    doadds,    0,    3,
  104.     "nntp addserver <nntpserver> <interval>",
  105.     "directory",    dondir,    0,    0,    NULLCHAR,
  106.     "dropserver",    dodrops,    0,    2,
  107.     "nntp dropserver <nntpserver>",
  108.     "groups",    dogroups,    0,    0,    NULLCHAR,
  109.     "kick",        dokicks,    0,    2,
  110.     "nntp kick <nntpserver>",
  111.     "listservers",    dolists,    0,    0,    NULLCHAR,
  112.     "quiet",    donnquiet,    0,    0,    NULLCHAR,
  113.     "trace",    donntrace,    0,    0,    NULLCHAR,
  114.     NULLCHAR,
  115. };
  116.  
  117. int
  118. donntp(argc,argv,p)
  119. int argc;
  120. char *argv[];
  121. void *p;
  122. {
  123.     return subcmd(Nntpcmds,argc,argv,p);
  124. }
  125.  
  126. static int
  127. doadds(argc,argv,p)
  128. int argc;
  129. char *argv[];
  130. void *p;
  131. {
  132.     struct nntpservers *np;
  133.     for(np = Nntpservers; np != NULLNNTP; np = np->next)
  134.         if(stricmp(np->name,argv[1]) == 0)
  135.             break;
  136.     if (np == NULLNNTP) {
  137.         np = (struct nntpservers *) callocw(1,sizeof(struct nntpservers));
  138.         np->name = strdup(argv[1]);
  139.         np->next = Nntpservers;
  140.         Nntpservers = np;
  141.         np->groups = NULLCHAR;
  142.         np->lowtime = np->hightime = -1;
  143.         np->nntpcli_t.func = nntptick;    /* what to call on timeout */
  144.         np->nntpcli_t.arg = (void *)np;
  145.     }
  146.     if (argc > 3) {
  147.         int i;
  148.         if (np->groups == NULLCHAR) {
  149.             np->groups = mallocw(NNTPMAXLEN);
  150.             *np->groups = '\0';
  151.         }
  152.         for (i = 3; i < argc; ++i) {
  153.             if (isdigit(*argv[i])) {
  154.                 int lh, ll, hh, hl;
  155.                 sscanf(argv[i], "%d:%d-%d:%d", &lh, &ll, &hh, &hl);
  156.                 np->lowtime = lh * 100 + ll;
  157.                 np->hightime = hh * 100 + hl;
  158.             } else if ((strlen(np->groups)+strlen(argv[i])+2) >= NNTPMAXLEN)
  159.                 tprintf("Group list too long!  Group '%s' ignored!\n", argv[i]);
  160.             else {  /* it's a group, and it fits... add it to list */
  161.                 if (*np->groups != '\0')
  162.                     strcat(np->groups, ",");
  163.                 strcat(np->groups, argv[i]);
  164.             }
  165.         }
  166.         if (*np->groups == '\0') {    /* No groups specified? */
  167.             free(np->groups);
  168.             np->groups = NULLCHAR;
  169.         }
  170.     }
  171.     /* set timer duration */
  172.     set_timer(&np->nntpcli_t,atol(argv[2])*1000L);
  173.     start_timer(&np->nntpcli_t);        /* and fire it up */
  174.     return 0;
  175. }
  176.  
  177. static int
  178. dodrops(argc,argv,p)
  179. int argc;
  180. char *argv[];
  181. void *p;
  182. {
  183.     struct nntpservers *np, *npprev = NULLNNTP;
  184.     for(np = Nntpservers; np != NULLNNTP; npprev = np, np = np->next)
  185.         if(stricmp(np->name,argv[1]) == 0) {
  186.             stop_timer(&np->nntpcli_t);
  187.             free(np->name);
  188.             if (np->groups)
  189.                 free(np->groups);
  190.             if(npprev != NULLNNTP)
  191.                 npprev->next = np->next;
  192.             else
  193.                 Nntpservers = np->next;
  194.             free((char *)np);
  195.             return 0;
  196.     }
  197.     tprintf("No such server enabled.\n");
  198.     return 0;
  199. }
  200.  
  201. static int
  202. dolists(argc,argv,p)
  203. int argc;
  204. char *argv[];
  205. void *p;
  206. {
  207.     struct nntpservers *np;
  208.     for(np = Nntpservers; np != NULLNNTP; np = np->next) {
  209.         char tbuf[80];
  210.         if (np->lowtime != -1 && np->hightime != -1)
  211.             sprintf(tbuf, " -- %02d:%02d-%02d:%02d", np->lowtime/100, np->lowtime%100, np->hightime/100, np->hightime%100);
  212.         else
  213.             tbuf[0] = '\0';
  214.         tprintf("%-32s (%lu/%lu%s) %s\n", np->name,
  215.             read_timer(&np->nntpcli_t) /1000L,
  216.             dur_timer(&np->nntpcli_t) /1000L,
  217.             tbuf, np->groups ? np->groups : "");
  218.     }
  219.     return 0;
  220. }
  221.  
  222. static int donntrace(argc, argv, p)
  223. int argc;
  224. char *argv[];
  225. void *p;
  226. {
  227.     return setshort(&nntptrace,"NNTP tracing",argc,argv);
  228. }
  229.     
  230. static int donnquiet(argc, argv, p)
  231. int argc;
  232. char *argv[];
  233. void *p;
  234. {
  235.     return setbool(&nntpquiet,"NNTP quiet",argc,argv);
  236. }
  237.     
  238. static int dondir(argc, argv, p)
  239. int argc;
  240. char *argv[];
  241. void *p;
  242. {
  243.     if (argc < 2) {
  244.         int i;
  245.         tprintf("spool: %s\n", News_spool ? News_spool : Mailspool);
  246.         tprintf("control: %s\n", Newsdir);
  247.         for (i = 0; i < MAXGROUPDIRS; ++i)
  248.             if (groupdirs[i].prefix)
  249.                 tprintf("%-10.10s %s\n", groupdirs[i].prefix, groupdirs[i].directory);
  250.     } else {
  251.         char *p;
  252.         if ((p = strchr(argv[1], '=')) != NULLCHAR) {  /* set a groupdir */
  253.             int i;
  254.             *p++ = '\0';
  255.             for (i = 0; i < MAXGROUPDIRS; ++i)
  256.                 if (groupdirs[i].prefix)
  257.                     if (!strnicmp(groupdirs[i].prefix, argv[1], strlen(argv[1]))) {
  258.                         if (groupdirs[i].directory) {
  259.                             free(groupdirs[i].directory);
  260.                             groupdirs[i].directory = NULLCHAR;
  261.                             }
  262.                         if (*p == '\0') {
  263.                             free(groupdirs[i].prefix);
  264.                             groupdirs[i].prefix = NULLCHAR;
  265.                         } else
  266.                             groupdirs[i].directory = strdup(p);
  267.                         return 0;
  268.                     }
  269.             if (*p == '\0')  /* trashing a group that's not there */
  270.                 return 0;
  271.             for (i = 0; i < MAXGROUPDIRS; ++i){
  272.                 if (groupdirs[i].prefix == NULLCHAR) {
  273.                     groupdirs[i].prefix = strdup(argv[1]);
  274.                     if (groupdirs[i].directory) {
  275.                         free(groupdirs[i].directory);
  276.                         groupdirs[i].directory = NULL;
  277.                     }
  278.                     groupdirs[i].directory = strdup(p);
  279.                     return 0;
  280.                 }
  281.             }
  282.             tprintf("Directory table full\n");
  283.         }
  284.         else {  /* no '=', so just set default */
  285.             if (News_spool)
  286.                 free(News_spool);
  287.             News_spool = strdup(argv[1]);
  288.         }
  289.         if (argc > 2) {  /* they specified a newsdir as well */
  290.             if (np_all)
  291.                 free(Newsdir);
  292.             Newsdir = strdup(argv[2]);
  293.             np_all = 1;
  294.         }
  295.     }
  296.     return 0;
  297. }
  298.     
  299. static int
  300. dokicks(argc,argv,p)
  301. int argc;
  302. char *argv[];
  303. void *p;
  304. {
  305.     struct nntpservers *np;
  306.     for(np = Nntpservers; np != NULLNNTP; np = np->next)
  307.         if(stricmp(np->name,argv[1]) == 0) {
  308.             /* If the timer is not running, the timeout function has
  309.             * already been called and we don't want to call it again.
  310.             */
  311.             if(run_timer(&np->nntpcli_t)) {
  312.                 stop_timer(&np->nntpcli_t);
  313.             }
  314.             nntptick((void *)np);
  315.             return 0;
  316.     }
  317.     tprintf("No such server enabled.\n");
  318.     return 1;
  319. }
  320.  
  321. static int
  322. dogroups(argc,argv,p)
  323. int argc;
  324. char *argv[];
  325. void *p;
  326. {
  327.     int i;
  328.     if(argc < 2) {
  329.         if(Nntpgroups == NULLCHAR || (Nntpgroups != NULLCHAR && strcmp(Nntpgroups,"*") == 0))
  330.             tprintf("All groups are currently enabled.\n");
  331.         else
  332.             tprintf("Currently enabled newsgroups:\n%s\n",Nntpgroups);
  333.         return 0;
  334.     }
  335.     if(Nntpgroups == NULLCHAR)
  336.         Nntpgroups = mallocw(NNTPMAXLEN);
  337.     *Nntpgroups = '\0';
  338.     for(i=1; i < argc; ++i) {
  339.         if(i > 1)
  340.             strcat(Nntpgroups,",");
  341.         strcat(Nntpgroups,argv[i]);
  342.     }
  343.     return 0;
  344. }
  345.  
  346. /* This is the routine that gets called every so often to connect to
  347.  * NNTP servers.
  348.  */
  349. static void
  350. nntptick(tp)
  351. void *tp;
  352. {
  353.     newproc("NNTP client", 3072, nntp_job, 0, tp, NULL,0);
  354. }
  355.  
  356. static void
  357. nntp_job(i1,tp,v1)
  358. int i1;
  359. void *tp, *v1;
  360. {
  361.     FILE *fp, *tmpf;
  362.     int s = -1, i;
  363. /*    long pos; */
  364.     struct tm *ltm;
  365.     time_t t;
  366.     int now;
  367.     struct nntpservers *np = (struct nntpservers *) tp;
  368.     struct sockaddr_in fsocket;
  369.     char tbuf[NNTPMAXLEN], buf[NNTPMAXLEN], *cp, *lastdate = NULLCHAR;
  370.     if (nntptrace >= 3)
  371.         tprintf("NNTP daemon entered, target = %s\n",np->name);
  372.     if(availmem() < Memthresh){
  373.         if (nntptrace >= 2)
  374.             tprintf("NNTP daemon quit -- low memory\n");
  375.         /* Memory is tight, don't do anything */
  376.         start_timer(&np->nntpcli_t);
  377.         return;
  378.     }
  379.  
  380.     time(&t);    /* more portable than gettime() */
  381.     ltm = localtime(&t);
  382.     now = ltm->tm_hour * 100 + ltm->tm_min;
  383.     if (np->lowtime < np->hightime) {  /* doesn't cross midnight */
  384.         if (now < np->lowtime || now >= np->hightime) {
  385.             if (nntptrace >= 3)
  386.                 tprintf("NNTP window to '%s' not open\n", np->name);
  387.             start_timer(&np->nntpcli_t);
  388.             return;
  389.         }
  390.     } else {
  391.         if (now < np->lowtime && now >= np->hightime) {
  392.             if (nntptrace >= 3)
  393.                 tprintf("NNTP window to '%s' not open\n", np->name);
  394.             start_timer(&np->nntpcli_t);
  395.             return;
  396.         }
  397.     }
  398.  
  399.     fsocket.sin_addr.s_addr = resolve(np->name);
  400.     if(fsocket.sin_addr.s_addr == 0) {  /* No IP address found */
  401.         if (nntptrace >= 2)
  402.             tprintf("NNTP can't resolve host '%s'\n", np->name);
  403.         /* Try again later */
  404.         start_timer(&np->nntpcli_t);
  405.         return;
  406.     }
  407.     fsocket.sin_family = AF_INET;
  408.     fsocket.sin_port = IPPORT_NNTP;
  409.  
  410.     s = socket(AF_INET,SOCK_STREAM,0);
  411.     sockmode(s,SOCK_ASCII);
  412.     if(connect(s,(char *)&fsocket,SOCKSIZE) == -1){
  413.         cp = sockerr(s);
  414.         log(s,"NNTP %s Connect failed: %s",psocket(&fsocket),
  415.             cp != NULLCHAR ? cp : "");
  416.         if (nntptrace >= 2)
  417.             tprintf("NNTP %s Connect failed: %s\n",psocket(&fsocket),
  418.         cp != NULLCHAR ? cp : "");
  419.         goto quit;
  420.     }
  421.     /* Eat the banner */
  422.     i = getreply(s);
  423.     if(i == -1 || i >= 400) {
  424.         log(s,"NNTP %s bad reply on banner (response was %d)",psocket(&fsocket),i);
  425.         if (nntptrace >= 1)
  426.             tprintf("NNTP %s bad reply on banner (response was %d)\n",psocket(&fsocket),i);
  427.         goto quit;
  428.     }
  429.  
  430.     if (mlock(Newsdir, "nntp")) {
  431.         if (nntptrace >= 2)
  432.             tprintf("NNTP %s Connect failed: cannot lock nntp.dat\n", psocket(&fsocket));
  433.         goto quit;
  434.     }
  435.     sprintf(buf,"%s/nntp.dat",Newsdir);
  436.     if((fp = fopen(buf,APPEND_TEXT)) == NULLFILE) {
  437.         log(s,"NNTP %s Connect failed: Cannot open %s",psocket(&fsocket),
  438.             buf);
  439.         if (nntptrace >= 1)
  440.             tprintf("NNTP %s Connect failed: Cannot open %s\n",psocket(&fsocket), buf);
  441.         rmlock(Newsdir, "nntp");
  442.         goto quit;
  443.     }
  444.     rewind(fp);
  445. /*    for(pos=0L; fgets(buf,NNTPMAXLEN,fp) != NULLCHAR;pos=ftell(fp)) { */
  446.     for(; fgets(buf,NNTPMAXLEN,fp) != NULLCHAR;) {
  447.         if((cp = strchr(buf,' ')) == NULLCHAR)
  448.             continue;    /* something wrong with this line, skip it */
  449.         *cp = '\0';
  450.         if(stricmp(buf,np->name) == 0) {
  451.             rip(cp+1);
  452.             lastdate = strdup(cp+1);
  453.             break;
  454.         }
  455.     }
  456.     fclose(fp);
  457.     rmlock(Newsdir, "nntp");
  458.  
  459.     if(lastdate == NULLCHAR)
  460.         lastdate = strdup("700101 000000");
  461.     /* snapshot the time for use later in re-writing nntp.dat */
  462.     time(&t);
  463.     ltm = localtime(&t);
  464.                 
  465.     /* Get a list of new message-id's */
  466.     if (np->groups) {
  467.         if (nntptrace >= 3)
  468.             tprintf("==>NEWNEWS %s %s\n", np->groups, lastdate);
  469.         usprintf(s,"NEWNEWS %s %s\n", np->groups, lastdate);
  470.     } else {
  471.         if (nntptrace >= 3)
  472.             tprintf("==>NEWNEWS %s %s\n", Nntpgroups != NULLCHAR ? Nntpgroups : "*", lastdate);
  473.         usprintf(s,"NEWNEWS %s %s\n",Nntpgroups != NULLCHAR ? Nntpgroups : "*", lastdate);
  474.     }
  475.     free(lastdate);
  476.     /* Get the response */
  477.     if((i = getreply(s)) != 230) { /* protocol error */
  478.         log(s,"NNTP %s protocol error (response was %d)",psocket(&fsocket),i);
  479.         if (nntptrace >= 1)
  480.             tprintf("NNTP %s protocol error (response was %d)\n",psocket(&fsocket),i);
  481.         goto quit;
  482.     }
  483.     if((tmpf = tmpfile()) == NULLFILE) {
  484.         if (nntptrace >= 1)
  485.             tprintf("NNTP %s Cannot open temp file\n", psocket(&fsocket));
  486.         goto quit;
  487.     }
  488.     if(gettxt(s,tmpf) == -1) {
  489.         log(s, "NNTP %s giving up: gettxt() failure",psocket(&fsocket));
  490.         if (nntptrace >= 1)
  491.             tprintf("NNTP %s giving up: gettxt() failure\n",psocket(&fsocket));
  492.         fclose(tmpf);
  493.         goto quit;
  494.     }
  495.  
  496.     /* Open the history file */
  497.     if (mlock(Newsdir, "history")) {
  498.         if (nntptrace >= 1)
  499.             tprintf("NNTP %s giving up: couldn't lock history file\n", psocket(&fsocket));
  500.         fclose(tmpf);
  501.         goto quit;
  502.     }
  503.     sprintf(buf,"%s/history",Newsdir);
  504.     if((fp = fopen(buf,APPEND_TEXT)) == NULLFILE) {
  505.         log(s,"NNTP %s Connect failed: Cannot open %s",psocket(&fsocket), buf);
  506.         if (nntptrace >= 1)
  507.             tprintf("NNTP %s Connect failed: Cannot open %s\n",psocket(&fsocket), buf);
  508.         fclose(tmpf);
  509.         goto quit;
  510.     }
  511.     /* search through the history file for matching message id's */
  512.     rewind(tmpf);
  513.     while(fgets(tbuf,NNTPMAXLEN,tmpf) != NULLCHAR) {
  514.         i = 0;
  515.         rewind(fp);
  516.         while(fgets(buf,NNTPMAXLEN,fp) != NULLCHAR) {
  517.             if(stricmp(buf,tbuf) == 0) {
  518.                 i = 1;
  519.                 break;
  520.             }
  521.             pwait(NULL);
  522.         }
  523.         if(i == 0) {        /* not found, get the article */
  524.             if(getarticle(s,tbuf) == -1) {
  525.                 log(s,"NNTP %s Giving up: could not get article",psocket(&fsocket));
  526.                 if (nntptrace >= 2)
  527.                     tprintf("NNTP %s Giving up: could not get article\n",psocket(&fsocket));
  528.                 fclose(fp);
  529.                 rmlock(Newsdir, "history");
  530.                 fclose(tmpf);
  531.                 goto quit;
  532.             }
  533.             fprintf(fp,"%s",tbuf); /* add the new message id */
  534.         }
  535.     }
  536.     fclose(fp);
  537.     rmlock(Newsdir, "history");
  538.     fclose(tmpf);
  539.     if (nntptrace >= 3)
  540.         tprintf("==>QUIT\n");
  541.     usprintf(s,"QUIT\n");
  542.     /* Eat the response */
  543.     getreply(s);
  544.     /* NOW, update the nntp.dat file */
  545.     if (mlock(Newsdir, "nntp")) {
  546.         if (nntptrace >= 2)
  547.             tprintf("NNTP %s Could not lock nntp.dat for update\n", psocket(&fsocket));
  548.         goto quit;
  549.     }
  550.     sprintf(buf,"%s/nntp.dat",Newsdir);
  551.     fp = fopen(buf,READ_TEXT);
  552.     sprintf(buf, "%s/nntp.tmp",Newsdir);
  553.     if ((tmpf = fopen(buf, WRITE_TEXT)) == NULLFILE)
  554.         if (nntptrace >= 1)
  555.             tprintf("NNTP %s Cannot create temp file '%s'\n", psocket(&fsocket), buf);
  556.     if (fp == NULLFILE || tmpf == NULLFILE) {
  557.         log(s,"NNTP %s Could not update %s", psocket(&fsocket), buf);
  558.         if (nntptrace >= 2)
  559.             tprintf("NNTP %s Could not update %s\n",psocket(&fsocket), buf);
  560.         if (fp)
  561.             fclose(fp);
  562.         if (tmpf)
  563.             fclose(tmpf);
  564.         rmlock(Newsdir, "nntp");
  565.         goto quit;
  566.     }
  567.     while (fgets(tbuf, sizeof(tbuf), fp))
  568.         if (strnicmp(tbuf, np->name, strlen(np->name)))
  569.             fputs(tbuf, tmpf);
  570.     fprintf(tmpf,"%s %02d%02d%02d %02d%02d%02d\n",np->name,ltm->tm_year%100,ltm->tm_mon+1,
  571.         ltm->tm_mday,ltm->tm_hour,ltm->tm_min,ltm->tm_sec);
  572.     fclose(fp);
  573.     fclose(tmpf);
  574.     sprintf(buf, "%s/nntp.dat", Newsdir);
  575.     sprintf(tbuf, "%s/nntp.tmp", Newsdir);
  576.     unlink(buf);
  577.     rename(tbuf, buf);
  578.     rmlock(Newsdir, "nntp");
  579. quit:
  580.     if (nntptrace >= 3)
  581.         tprintf("NNTP daemon exiting\n");
  582.     close_s(s);
  583.     /* Restart timer */
  584.     start_timer(&np->nntpcli_t);
  585.     return;
  586. }
  587.  
  588. static int
  589. gettxt(s,fp)
  590. int s;
  591. FILE *fp;
  592. {
  593.     char buf[NNTPMAXLEN];
  594.     int nlines;
  595.     for (nlines = 0; recvline(s,buf,NNTPMAXLEN) != -1; ++nlines) {
  596.         if (nntptrace >= 4)
  597.             tprintf("<==%s", buf);
  598.         if(strcmp(buf,".\n") == 0) {
  599.             if (nntptrace >= 3)
  600.                 tprintf("NNTP received %d lines\n", nlines);
  601.             return 0;
  602.             }
  603.         /* check for escaped '.' characters */
  604.         if(strcmp(buf,"..\n") == 0)
  605.             fputs(".\n",fp);
  606.         else
  607.             fputs(buf,fp);
  608.     }
  609.     if (nntptrace >= 1)
  610.         tprintf("NNTP receive error after %d lines\n", nlines);
  611.     return -1;
  612. }
  613.  
  614. static int
  615. getreply(s)
  616. int s;
  617. {
  618.     char buf[NNTPMAXLEN];
  619.     int response;
  620.     while(recvline(s,buf,NNTPMAXLEN) != -1) {
  621.         /* skip informative messages and blank lines */
  622.         if(buf[0] == '\0' || buf[0] == '1')
  623.             continue;
  624.         sscanf(buf,"%d",&response);
  625.         if (nntptrace >= 3)
  626.             tprintf("<==%s\n", buf);
  627.         return response;
  628.     }
  629.     if (nntptrace >= 3)
  630.         tprintf("==No response\n");
  631.     return -1;
  632. }
  633.  
  634. static int
  635. getarticle(s,msgid)
  636. int s;
  637. char *msgid;
  638. {
  639.     char buf[NNTPMAXLEN], froml[NNTPMAXLEN], newgl[NNTPMAXLEN];
  640.     FILE *fp, *tmpf;
  641.     int r;
  642.     char *cp;
  643.     extern int Smtpquiet;
  644.  
  645.     if (nntptrace >= 3)
  646.         tprintf("==>ARTICLE %s", msgid);
  647.     usprintf(s,"ARTICLE %s", msgid);
  648.     r = getreply(s);
  649.     if(r == -1 || r >= 500)
  650.         return -1;
  651.     if(r >= 400)
  652.         return 0;
  653.     if((tmpf = tmpfile()) == NULLFILE) {
  654.         if (nntptrace >= 1)
  655.             tprintf("NNTP Cannot open temp file for article\n");
  656.         return -1;
  657.     }
  658.     if(gettxt(s,tmpf) == -1) {
  659.         fclose(tmpf);
  660.         return -1;
  661.     }
  662.     /* convert the article into mail format */
  663.     rewind(tmpf);
  664.     froml[0] = '\0';
  665.     newgl[0] = '\0';
  666.     while(fgets(buf,NNTPMAXLEN,tmpf) != NULLCHAR) {
  667.         if(strncmp(buf,"From: ",6) == 0) {
  668.             struct timeb t;
  669.             ftime(&t);
  670.             rip(&buf[6]);
  671.             sprintf(froml,"From %s %ld\n",&buf[6], t.time);
  672.             if(newgl[0] != '\0')
  673.                 break;
  674.         }
  675.         if(strncmp(buf,"Newsgroups: ",12) == 0) {
  676.             strcpy(newgl,&buf[12]);
  677.             if(froml[0] != '\0')
  678.                 break;
  679.         }
  680.         /* invalid article - missing 'From:' line or 'Newsgroups:' line */
  681.         if(strcmp(buf,"\n") == 0 && (froml[0] == '\0' || newgl[0] == '\0')) {
  682. /*            fclose(fp); */
  683.             fclose(tmpf);
  684.             return 0;
  685.         }
  686.     }
  687.     sprintf(buf,"%s/",News_spool ? News_spool : Mailspool);
  688.     for(cp=newgl;;++cp) {
  689.         if(*cp == '.') {
  690. #ifdef __TURBOC__
  691.             mkdir(buf); /* create a subdirectory, if necessary */
  692. #else
  693.             mkdir(buf,0755); /* create a subdirectory, if necessary */
  694. #endif
  695.             strcat(buf,"/");
  696.             continue;
  697.         }
  698.         if(*cp == ',' || *cp == '\n') {
  699.             char tempdir[80], prefix[20], *p;
  700.             strcpy(tempdir, buf);
  701.             if ((p = strrchr(tempdir, '/')) != NULLCHAR) {
  702.                 *p++ = '\0';
  703.                 strcpy(prefix, p);
  704.             }
  705.             if (mlock(tempdir, prefix)) {
  706.                 if (nntptrace >= 2)
  707.                     tprintf("NNTP group '%s' is locked\n", buf);
  708.                 return -1;
  709.             }
  710.             strcat(buf,".txt");
  711.             /* open the mail file */
  712.             if (nntptrace >= 3)
  713.                 tprintf("Writing article to '%s'\n", buf);
  714.             if((fp = fopen(buf,APPEND_TEXT)) != NULLFILE) {
  715.                 fputs(froml,fp);
  716.                 rewind(tmpf);
  717.                 while(fgets(buf,NNTPMAXLEN,tmpf) != NULLCHAR) {
  718.                     /* for UNIX mail compatiblity */
  719.                     if(strncmp(buf,"From ",5) == 0)
  720.                         putc('>',fp);
  721.                     fputs(buf,fp);
  722.                 }
  723.                 putc('\n',fp);
  724.                 fclose(fp);
  725.             }
  726.             rmlock(tempdir, prefix);
  727.             if (*cp == '\n') 
  728.                 break;
  729.             else
  730.                 sprintf(buf,"%s/",News_spool ? News_spool : Mailspool);
  731.             continue;
  732.         }
  733.         buf[strlen(buf)+1] = '\0';
  734.         buf[strlen(buf)] = strchr(validchars, tolower(*cp)) ? *cp : '_';
  735.     }
  736.     fclose(tmpf);
  737.     strcpy(buf,msgid);        /* Get a copy we can munge */
  738.     rip(buf);            /* remove trailing new-line */
  739.     rip(newgl);            /* ditto */
  740.     if(!nntpquiet)
  741.         tprintf("New news arrived: %s, article %s%c\n",newgl,buf,Smtpquiet?' ':'\007');
  742.     return 0;
  743. }
  744.